/**
 * 常用工具集
 */

// 深拷贝
export const extend = function (obj) {
  const ss = JSON.parse(JSON.stringify(obj))
  return ss
}

// 倒计时器
/**
  * 倒计时器,该函数将返回一个countdown对象.
  * countdown对象拥有stop(),restart(),start(),done(callback),timeout()方法
  * 注意
  * 1.最后一次为0
  * 2.首次执行必定在延时之后
  * 3.二个参数必须为 number,不然会一直执行
  *
  * 参数
  * @param  {number} n        [倒数读秒]
  * @param  {number} interval [间隔]
  *
  * 示例
  * 无无限循环
   var ss = tool.Countdown(-1,1000).done(function (i) {
     console.log(i);
   });

  * 不立即执行
     var ss = tool.Countdown(10,1000).stop().done(function (i) {
     console.log(i);
   });

  * 特定条件停止
     var ss = tool.Countdown(10,1000).done(function (i) {
     console.log(i);
     if (i < 5) {
       this.stop();
     };
   });
  */
export const countdown = function (n, interval) {
  interval = interval || 1000
  var countdown = {
    idx: 0,
    max: n,
    i: n,
    callback: function () {},
    stop: function () {
      clearTimeout(this.idx)
      return this
    },
    restart: function () {
      this.i = this.max
      this.timeout()
      return this
    },
    start: function () {
      this.timeout()
      return this
    },
    timeout: function () {
      let self = this
      self.idx = setTimeout(function () {
        // 当 tool.countdown(-1,1000) 时可以一直执行
        if (self.i === 0) {
          return self
        }

        self.i--
        self.timeout()
        self.callback.apply(countdown, [self.i])
      }, interval)
      return self
    },
    done: function (callback) {
      this.callback = callback
      return this
    },
  }
  countdown.timeout()
  return countdown
}

// 快速排序 min-max
/**
 * 快速排序 min-max
 * by oTwo 2014年6月17日 13:33:29
 * @param  {array} d [description]
 * @return {array}
 *
 * ps:不改变原集合
 */
export const quickSort = function (d) {
  if (d.length <= 1) {
    return d
  }

  let k = d[0]
  let big = []
  let small = []
  let i
  let len
  for (i = 1, len = d.length; i < len; i++) {
    if (d[i] < k) {
      small.push(d[i])
    } else {
      big.push(d[i])
    }
  }
  small = quickSort(small)
  big = quickSort(big)

  return [].concat(small, k, big)
}

// 生成指定范围内的随机整数
/**
 * 生成指定范围内的随机整数
 * by oTwo 2014年6月17日 13:33:35
 * @param  {number} begin [开始范围,默认 100, 可选]
 * @param  {number} end   [结束范围,默认 0, 可选]
 * @return {int}      [随机数]
 *
 * ps:把begin作为大数的原因是可以只写一个参数.roll(1000) === roll(1000,0)
 */
export const roll = function (begin, end) {
  begin = isNaN(begin - 0) ? 100 : begin
  end = isNaN(end - 0) ? 0 : end
  if (begin < end) {
    let ss = begin
    begin = end
    end = ss
  }

  let r = Math.random()
  r = r * (begin - end + 1) + end
  r = parseInt(r, 10)
  return r
}

// 取得地址档中GET的参数
/**
 * 取得地址档中GET的参数
 * @return {JSON} [description]
 */
export const request = function () {
  let request = {}
  let hash = ''
  let ss
  hash = window.location.search
  hash = hash.substr(1)

  // 在这里返回 undefined 要比返回空对象好一点,取值我们更容易发现出了什么问题
  if (!hash) {
    return
  }

  hash = decodeURIComponent(hash)
  hash = hash.split(/&/g)
  for (let f1 in hash) {
    ss = hash[f1].split(/=/g)
    request[ss[0]] = ss[1]
  }
  return request
}

// 格式化成时间字符串
/**
 * 格式化成时间字符串 简版,依赖 tool.pad
 * @param  {date/str/number} date   [时间对像,或能转为时间对像字符串/数字]
 * @param  {str} format ['Y年M月D日 hh:mm:ss']
 * @return {str}
 */
export const formatDate = function (date, format) {
  let d = new Date(date)
  let map = {
    Y: d.getFullYear(),
    M: d.getMonth() + 1,
    D: d.getDate(),
    hh: d.getHours(),
    mm: d.getMinutes(),
    ss: d.getSeconds(),
  }

  // 从左边补齐二位
  for (let f2 in map) {
    map[f2] = map[f2].toString().padStart(2, '0')
  }

  for (let f1 in map) {
    format = format.replace(f1, map[f1])
  }
  return format
}

// 进入全屏
export const requestFullScreen = function () {
  let de = document.documentElement
  if (de.requestFullscreen) {
    de.requestFullscreen()
  } else if (de.mozRequestFullScreen) {
    de.mozRequestFullScreen()
  } else if (de.webkitRequestFullScreen) {
    de.webkitRequestFullScreen()
  }
}

// 退出全屏
export const exitFullscreen = function () {
  let de = document
  if (de.exitFullscreen) {
    de.exitFullscreen()
  } else if (de.mozCancelFullScreen) {
    de.mozCancelFullScreen()
  } else if (de.webkitCancelFullScreen) {
    de.webkitCancelFullScreen()
  }
}

/**
 * 1 所有可以转为 false 的值 都是"空".
 * 2 深度检测对象,如果对象的所有值都是"空"则该对象是"空"
 *
 * by oTwo 2016年10月25日 11:54:57
 * @return {Boolean}   [description]
 */
export const isEmpty = function (d) {
  if (typeof d !== 'object') {
    return !d
  }
  let ss = true
  let f1
  for (f1 in d) {
    if (typeof d[f1] === 'object') {
      ss = isEmpty(d[f1])
    } else {
      ss = !d[f1]
    }

    if (ss === false) {
      return false
    }
  }
  return true
}

// 是否为验证码
export const isCode = function (code) {
  let reg = /^[\d]{6}$/
  return reg.test(code)
}

// 是否为手机
export const isPhone = function (phone) {
  let reg = /^1[2345789]\d{9}$/
  return reg.test(phone)
}

// 是否为邮箱
export const isEmail = function (email) {
  let reg =
    /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/
  return reg.test(email)
}

// 验证身份证信息
export const isCert = function (val) {
  const reg = new RegExp(
    /^[1-9]\d{5}(19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
  )

  const card = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
  const cardX = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
  let last = 0
  let sum = 0
  if (!reg.test(val)) {
    return false
  }

  // 校验位识别
  for (let i = 0, l = val.length; i < l; i++) {
    if (i < val.length - 1) {
      sum += val[i] * card[i]
    }
    if (i === val.length - 1) {
      last = val[i]
    }
  }
  return cardX[sum % 11] === last
}

// 统一社会信用代码
export const isCreditCode = function (val) {
  const reg = /^([0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10})$/
  return reg.test(val)
}

// 表单校验
// { validator: this.$tool.validateCreditCode, trigger: 'blur',message:'统一社会信用代码校验错误' }
export const validateCreditCode = (rule, value, callback) => {
  if (isCreditCode(value)) {
    callback()
  } else {
    callback(new Error(rule.message))
  }
}

// 数字 千分位逗号
export const toThousands = function (num, decimalPlaces = 2) {
  const number = parseFloat(num)
  if (isNaN(number)) {
    return 'Invalid number'
  }
  return number.toFixed(decimalPlaces).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}

// 将数组转为树
// tool.arrToTree(res.data, {id: 'id', pid: 'pid', children: 'children'})
// 返回的是个数组,数组其中的对象为树结构
export const arrToTree = function (data, config = {}) {
  let id = config.id || 'id'
  let pid = config.pid || 'pid'
  let children = config.children || 'children'
  let idMap = {}
  let jsonTree = []
  data.forEach(function (v) {
    idMap[v[id]] = v
  })
  data.forEach(function (v) {
    let parent = idMap[v[pid]]
    if (parent) {
      !parent[children] && (parent[children] = [])
      parent[children].push(v)
    } else {
      jsonTree.push(v)
    }
  })
  return jsonTree
}

// 将树转为数组
// tool.treeToArr(res.data)
// tool.treeToArr(res.data,{ children: 'children' })
// 返回的是个二组数组
export const treeToArr = function (
  data,
  config = { children: 'children' },
  pid,
) {
  data = extend(data)
  let arr = []
  data.forEach(function (item) {
    let d = extend(item)
    let children = item[config.children]
    delete d[config.children]
    d[config.pid] = pid
    arr.push(d)

    if (children && Array.isArray(children)) {
      arr = arr.concat(treeToArr(children, config, item.id))
    }
  })
  return arr
}

/**
 * 递归遍历树
 * @param {Object} tree - 包含树形结构的对象
 * @param {Function} callback - 可选的回调函数，用于处理每个节点
 * @example
 * // 示例：遍历树并打印每个节点的名称
 * const tree = {
 *   id: '1',
 *   name: 'Root',
 *   children: [
 *     { id: '2', name: 'Child 1', children: [] },
 *     { id: '3', name: 'Child 2', children: [] },
 *     { id: '4', name: 'Child 3', children: [] }
 *   ]
 * };
 *
 * traverseTree(tree, (node) => {
 *   console.log(node.name);
 * });
 */
export const traverseTree = function (tree, callback = function () {}) {
  // 处理当前节点
  callback(tree)

  // 递归遍历子节点
  if (tree.children) {
    tree.children.forEach((child) => {
      traverseTree(child, callback)
    })
  }
}

/**
 * 通过给定的 id 和树形数据结构 tree 构建包含自身及其所有父节点的路径数组，确保数组从根节点开始。
 *
 * @param {string} id - 要查找的节点的 id。
 * @param {Array} tree - 包含树形结构的数组，每个元素是一个对象，包含 id, name 和 pid 属性。
 * @returns {Array} - 包含从根节点到指定节点（包括自身）的所有节点的数组。
 *
 * 示例:
 *
 * ```javascript
 * const treeData = [
 *   { id: "1-1", name: "Root1", pid: null },
 *   { id: "1-2", name: "Child1", pid: "1-1" },
 *   { id: "2-14", name: "Grandchild1", pid: "1-2" },
 *   // 更多节点...
 * ];
 *
 * const nodeId = "2-14";
 * const pathNodes = findPathToRoot(nodeId, treeData);
 * console.log('Path to root for node with id:', nodeId, 'is:', pathNodes);
 * ```
 */
export function findPathToRoot(id, tree) {
  // 初始化路径数组
  let path = []

  // 在树中查找当前节点
  let currentNode = tree.find((node) => {
    return node.id === id
  })

  // 如果当前节点存在
  while (currentNode) {
    path.unshift(currentNode)

    // 如果当前节点是顶级节点，停止查找
    if (!currentNode) {
      break
    }

    // 否则，查找当前节点的父节点
    currentNode = tree.find((node) => {
      return node.id === currentNode.pid
    })
  }

  return path
}
